See https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=17847
(Though the original code needs to be patched to be case-insensitive.)

--- a/hosts_access.5
+++ b/hosts_access.5
@@ -89,6 +89,10 @@ An expression of the form `n.n.n.n/m.m.m
 bitwise AND of the address and the `mask\'. For example, the net/mask
 pattern `131.155.72.0/255.255.254.0\' matches every address in the
 range `131.155.72.0\' through `131.155.73.255\'.
+.IP \(bu
+Wildcards `*\' and `?\' can be used to match hostnames or IP addresses.  This
+method of matching cannot be used in conjunction with `net/mask\' matching,
+hostname matching beginning with `.\' or IP address matching ending with `.\'.
 .SH WILDCARDS
 The access control language supports explicit wildcards:
 .IP ALL
--- a/hosts_access.c
+++ b/hosts_access.c
@@ -82,6 +82,7 @@ static int client_match();
 static int host_match();
 static int string_match();
 static int masked_match();
+static int match_pattern_ylo();
 
 /* Size of logical line buffer. */
 
@@ -289,6 +290,11 @@ char   *string;
 {
     int     n;
 
+#ifndef DISABLE_WILDCARD_MATCHING
+    if (strchr(tok, '*') || strchr(tok,'?')) {  /* contains '*' or '?' */
+        return (match_pattern_ylo(string,tok)); 	       
+    } else 
+#endif    
     if (tok[0] == '.') {			/* suffix */
 	n = strlen(string) - strlen(tok);
 	return (n > 0 && STR_EQ(tok, string + n));
@@ -329,3 +335,78 @@ char   *string;
     }
     return ((addr & mask) == net);
 }
+
+#ifndef DISABLE_WILDCARD_MATCHING
+/* Note: this feature has been adapted in a pretty straightforward way
+   from Tatu Ylonen's last SSH version under free license by 
+   Pekka Savola <pekkas@netcore.fi>.
+
+   Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+*/
+
+/* Returns true if the given string matches the pattern (which may contain
+   ? and * as wildcards), and zero if it does not match. */
+	  
+static int match_pattern_ylo(const char *s, const char *pattern)
+{
+  char src;
+  char pat;
+  while (1)
+    {
+      /* If at end of pattern, accept if also at end of string. */
+      if (!*pattern)
+        return !*s;
+
+      /* Process '*'. */
+      if (*pattern == '*')
+        {
+	  /* Skip the asterisk. */
+	  pattern++;
+
+	  /* If at end of pattern, accept immediately. */
+          if (!*pattern)
+            return 1;
+
+	  /* If next character in pattern is known, optimize. */
+          if (*pattern != '?' && *pattern != '*')
+            {
+	      /* Look instances of the next character in pattern, and try
+		 to match starting from those. */
+              pat = *pattern;
+              for (; *s; s++) {
+                src = *s;
+                if (toupper(src) == toupper(pat) &&
+                    match_pattern_ylo(s + 1, pattern + 1))
+                  return 1;
+              }
+	      /* Failed. */
+              return 0;
+            }
+
+	  /* Move ahead one character at a time and try to match at each
+	     position. */
+          for (; *s; s++)
+            if (match_pattern_ylo(s, pattern))
+              return 1;
+	  /* Failed. */
+          return 0;
+        }
+
+      /* There must be at least one more character in the string.  If we are
+	 at the end, fail. */
+      if (!*s)
+        return 0;
+
+      /* Check if the next character of the string is acceptable. */
+      pat = *pattern;
+      src = *s;
+      if (*pattern != '?' && toupper(pat) != toupper(src))
+	return 0;
+      
+      /* Move to the next character, both in string and in pattern. */
+      s++;
+      pattern++;
+    }
+  /*NOTREACHED*/
+}
+#endif /* DISABLE_WILDCARD_MATCHING */
